package com.hzlj.position.locate.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fz.common.base.core.JsonUtils;
import com.fz.common.base.exception.PubException;
import com.google.common.collect.Maps;
import com.hzlj.common.message.common.dto.message.MessageNoticeResultDTO;
import com.hzlj.common.message.common.dto.message.MessageNoticeUserDTO;
import com.hzlj.common.message.common.util.MessageRestUtil;
import com.hzlj.position.config.config.PositionConfig;
import com.hzlj.position.locate.common.dto.messageToday.MessageItemDTO;
import com.hzlj.position.locate.common.dto.messageToday.MessageTodayAddDTO;
import com.hzlj.position.locate.common.dto.messageToday.MessageTodayNoticeUsersDTO;
import com.hzlj.position.locate.common.dto.reportCommon.ReportNoticeResultDTO;
import com.hzlj.position.locate.common.model.MessageToday;
import com.hzlj.position.locate.dao.MessageTodayDao;
import com.hzlj.position.locate.service.MessageTemplateBussService;
import com.hzlj.position.locate.service.MessageTodayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.hzlj.position.config.config.IdConfig.POSITION_ID_GEN;
import static com.hzlj.position.locate.service.MessageTemplateBussService.classOfMessageTemplate;

/**
 * 当天消息通知(MessageToday)表服务接口
 *
 * @author makejava
 * @date 2024-01-10 16:38:24
 */
@Slf4j
@Service
public class MessageTodayServiceImpl implements MessageTodayService {
    @Resource
    private MessageTodayDao messageTodayDao;
    @Resource
    private PositionConfig positionConfig;
    @Resource
    private ApplicationContext applicationContext;
    private static final Map<String, Object> EMPTY_MAP = Maps.newHashMap();

    @Override
    public void sendAsync(List<MessageTodayAddDTO> adds) {
        if(ObjectUtil.isEmpty(adds)){
            return;
        }
        List<MessageToday> messageTodays = this.convert(adds);
        this.saveBatch(messageTodays);
    }

    @Override
    public Map<String, MessageNoticeResultDTO> send(List<MessageTodayAddDTO> adds) {
        Map<String, MessageNoticeResultDTO> noticeResults = Maps.newHashMap();
        if(ObjectUtil.isEmpty(adds)){
            return noticeResults;
        }
        List<MessageToday> messageTodays = this.convert(adds);
        for (MessageToday message : messageTodays) {
            Map<String, MessageNoticeResultDTO> send = this.send(message, false);
            if (send != null) {
                noticeResults.putAll(send);
            }
        }
        return noticeResults;
    }


    @Override
    public int send() {
        Date now = new Date();
        List<MessageToday> messages = this.messageTodayDao.listMessageToday(now);
        if (ObjectUtil.isEmpty(messages)) {
            return 0;
        }
        //多线程
        log.info("开始发送消息,size={}", messages.size());
        CountDownLatch countDownLatch = new CountDownLatch(messages.size());
        ExecutorService executor = Executors.newFixedThreadPool(positionConfig.getCommonService().getThread());
        for (MessageToday message : messages) {
            executor.execute(() -> {
                try {
                    send(message, true);
                } catch (Exception e) {
                    log.error("消息发送失败/修改业务数据失败:message.serviceId={},messageTemplate={}", message.getServiceId(), message.getMessageTemplate(), e);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }

        try {
            countDownLatch.await();
            //4、删除已经发出去的消息
            this.messageTodayDao.removeByIds(
                    messages.stream().map(MessageToday::getId).collect(Collectors.toList())
            );
        } catch (Exception e) {
            log.error("删除消息报错", e);
        }
        return messages.size();
    }


    @Override
    @SuppressWarnings("unchecked")
    public ReportNoticeResultDTO send(String messageTemplate, List<MessageItemDTO> messages) {
        ReportNoticeResultDTO result = new ReportNoticeResultDTO();
        MessageTemplateBussService messageTemplateBussService = this.messageTemplateBussService(messageTemplate, true);
        assert messageTemplateBussService != null;
        for (MessageItemDTO message : messages) {
            Map<String, Object> params = message.getParams() != null ? JsonUtils.json2map(message.getParams()) : EMPTY_MAP;
            //1、发送通知
            MessageNoticeResultDTO notice = MessageRestUtil.noticeQueryUsers(
                    messageTemplate,
                    message.getServiceId(),
                    params,
                    message.getWorker(),
                    message.getUserId());
            //处理通知结果
            if (!notice.isFail()) {
                result.addSuccess(message.getServiceId(), message.getUserId(), message.getUserName(), notice.getBody());
            } else {
                result.addFail(message.getServiceId(), message.getUserId(), message.getUserName(), notice.getBody());
            }
            //2、修改业务表
            messageTemplateBussService.updateNoticeResult(messageTemplate, message.getServiceId(), message.getBelongDay(), null, notice);
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    private Map<String, MessageNoticeResultDTO> send(MessageToday message, boolean checkNeedSend) {
        MessageTemplateBussService messageTemplateBussService = this.messageTemplateBussService(message.getMessageTemplate(), false);
        if (messageTemplateBussService == null) {
            log.info("没有定义message业务类，不能发送：message.serviceId={},messageTemplate={}", message.getServiceId(), message.getMessageTemplate());
            return null;
        }
        Map<String, Object> messageParams = message.getMessageParams() != null ? JsonUtils.json2map(message.getMessageParams()) : EMPTY_MAP;
        Map<String, Object> checkParams = message.getCheckParams() != null ? JsonUtils.json2map(message.getCheckParams()) : EMPTY_MAP;
        //1、校验是否需要发送
        if (checkNeedSend) {
            boolean needSend = messageTemplateBussService.checkNeedSend(message.getMessageTemplate(), message.getServiceId(), message.getBelongDay(), messageParams, checkParams);
            if (!needSend) {
                log.info("不需要发送消息：message.serviceId={},messageTemplate={}", message.getServiceId(), message.getMessageTemplate());
                return null;
            }
        }
        //2、发送通知
        log.info("开始发送消息：message={}", JsonUtils.obj2json(message));
        MessageNoticeResultDTO notice = null;
        Map<String, MessageNoticeResultDTO> noticeResult = Maps.newHashMap();
        for (MessageNoticeUserDTO user : message.getMessageUsers()) {
            List<MessageNoticeResultDTO> notices = MessageRestUtil.noticeQueryUsers(
                    message.getMessageTemplate(),
                    message.getServiceId(),
                    messageParams,
                    user
            );
            Optional<MessageNoticeResultDTO> match = notices.stream()
                    .filter(e -> !e.isFail())
                    .findFirst();
            MessageNoticeResultDTO temp = match.orElseGet(() -> notices.get(0));
            notice = !temp.isFail() ? temp : ObjectUtil.defaultIfNull(notice, temp);
            noticeResult.put(notice.getServiceId(), notice);
        }
        //3、修改业务表
        messageTemplateBussService.updateNoticeResult(message.getMessageTemplate(), message.getServiceId(), message.getBelongDay(), checkParams, notice);
        return noticeResult;
    }


    @Override
    public void delete() {
        this.messageTodayDao.delete(new Date());
    }

    /**
     * 保存数据，会过滤过期的数据
     */
    private void saveBatch(List<MessageToday> messageTodays) {
        Date now = new Date();
        messageTodays = messageTodays.stream()
                .filter(Objects::nonNull)
                .filter(e -> {
                    boolean timed = DateUtil.between(now, e.getNoticeTime(), DateUnit.MINUTE, false) < -5;
                    if (timed) {
                        log.info("消息发送时间已经过期，被过滤掉:{}", JsonUtils.obj2json(e));
                    }
                    return !timed;
                })
                .collect(Collectors.toList());
        if (ObjectUtil.isNotEmpty(messageTodays)) {
            this.messageTodayDao.saveBatch(messageTodays);
        }
    }


    private List<MessageToday> convert(List<MessageTodayAddDTO> dtos) {
        return dtos.stream().map(e -> {
            MessageToday messageToday = this.convert(e);
            messageToday.setMessageUsers(MessageTodayNoticeUsersDTO.of()
                    .addUser(e.getUser())
            );
            return messageToday;
        }).collect(Collectors.toList());
    }

    private MessageToday convert(MessageTodayAddDTO dto) {
        MessageToday messageToday = BeanUtil.copyProperties(dto, MessageToday.class);
        messageToday.setId(POSITION_ID_GEN.nextIdStr());
        messageToday.setCreateTime(new Date());
        return messageToday;
    }

    private MessageTemplateBussService messageTemplateBussService(String messageTemplate, boolean exceptionIfAbsent) {
        Class<? extends MessageTemplateBussService> clazz = classOfMessageTemplate.get(messageTemplate);
        if (clazz != null) {
            return applicationContext.getBean(StrUtil.lowerFirst(clazz.getSimpleName()),clazz);
        }
        for (Map.Entry<String, Class<? extends MessageTemplateBussService>> entry : classOfMessageTemplate.entrySet()) {
            String key = entry.getKey();
            Matcher matcher = Pattern.compile("^"+key+"$").matcher(messageTemplate);
            if (matcher.find()) {
                return applicationContext.getBean(StrUtil.lowerFirst(entry.getValue().getSimpleName()),entry.getValue());
            }
        }
        log.warn("没有定义message业务类:{}", messageTemplate);
        if (exceptionIfAbsent) {
            throw new PubException("没有定义message业务类:" + messageTemplate);
        }
        return null;

    }
}
